package xyz.fivefaith.tool.json.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import lombok.extern.slf4j.Slf4j;

import xyz.fivefaith.tool.common.domain.enums.SensitiveStrategy;
import xyz.fivefaith.tool.json.sensitive.Sensitive;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据脱敏json序列化工具
 *
 * @author zac
 * @since 2023/12/18 13:49
 */
@Slf4j
public class SensitiveJsonSerializer extends JsonSerializer<String>
        implements ContextualSerializer {

    private static final ConcurrentHashMap<SensitiveStrategy, SensitiveJsonSerializer> CACHE =
            new ConcurrentHashMap<>();

    private final SensitiveStrategy strategy;

    /* ---------- 构造器 ---------- */
    public SensitiveJsonSerializer() {
        this(null);
    }

    private SensitiveJsonSerializer(SensitiveStrategy strategy) {
        this.strategy = strategy;
    }

    /* ---------- 序列化 ---------- */
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException {
        if (strategy != null && value != null) {
            gen.writeString(strategy.desensitizer().apply(value));
        } else {
            gen.writeString(value);
        }
    }

    /* ---------- 上下文感知 ---------- */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
            throws JsonMappingException {

        if (property == null) {
            return prov.findValueSerializer(String.class, property);
        }

        Sensitive anno = property.getAnnotation(Sensitive.class);
        if (anno != null && property.getType().getRawClass() == String.class) {
            SensitiveStrategy key = anno.strategy();
            return CACHE.computeIfAbsent(key, SensitiveJsonSerializer::new);
        }

        return prov.findValueSerializer(property.getType(), property);
    }
}
